home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / mac / files / t_sys5 / 92052tar.gz / 920528.tar / tcpout.c < prev    next >
C/C++ Source or Header  |  1992-05-28  |  7KB  |  237 lines

  1. /* @(#) $Header: tcpout.c,v 1.7 92/05/28 13:50:36 deyke Exp $ */
  2.  
  3. /* TCP output segment processing
  4.  * Copyright 1991 Phil Karn, KA9Q
  5.  */
  6. #include "global.h"
  7. #include "timer.h"
  8. #include "mbuf.h"
  9. #include "netuser.h"
  10. #include "internet.h"
  11. #include "tcp.h"
  12. #include "ip.h"
  13.  
  14. static double mybackoff __ARGS((int n));
  15.  
  16. static double mybackoff(n)
  17. int n;
  18. {
  19.   double  b;
  20.  
  21.   for (b = 1.0; n > 0; b *= 1.25, n--) ;
  22.   return b;
  23. }
  24.  
  25. /* Send a segment on the specified connection. One gets sent only
  26.  * if there is data to be sent or if "force" is non zero
  27.  */
  28. void
  29. tcp_output(tcb)
  30. register struct tcb *tcb;
  31. {
  32.     struct pseudo_header ph;/* Pseudo-header for checksum calcs */
  33.     struct mbuf *hbp,*dbp;  /* Header and data buffer pointers */
  34.     int16 hsize;            /* Size of header */
  35.     struct tcp seg;         /* Local working copy of header */
  36.     int16 ssize;            /* Size of current segment being sent,
  37.                  * including SYN and FIN flags */
  38.     int16 dsize;            /* Size of segment less SYN and FIN */
  39.     int16 usable;           /* Usable window */
  40.     int16 sent;             /* Sequence count (incl SYN/FIN) already
  41.                  * in the pipe but not yet acked */
  42.     int32 rto;              /* Retransmit timeout setting */
  43.  
  44.     if(tcb == NULLTCB)
  45.         return;
  46.  
  47.     switch(tcb->state){
  48.     case TCP_LISTEN:
  49.     case TCP_CLOSED:
  50.         return; /* Don't send anything */
  51.     }
  52.     for(;;){
  53.         /* Compute data already in flight */
  54.         sent = tcb->snd.ptr - tcb->snd.una;
  55.  
  56.         /* If transmitter has been idle for more than a RTT,
  57.          * take the congestion window back down to one packet.
  58.          */
  59.         if(!run_timer(&tcb->timer)
  60.          && (msclock() - tcb->lastactive) > tcb->srtt)
  61.             tcb->cwind = tcb->mss;
  62.  
  63.         /* Compute usable send window as minimum of offered
  64.          * and congestion windows, minus data already in flight.
  65.          * Be careful that the window hasn't shrunk --
  66.          * these are unsigned vars.
  67.          */
  68.         usable = min(tcb->snd.wnd,tcb->cwind);
  69.         if(usable > sent)
  70.             usable -= sent; /* Most common case */
  71.         else if(usable == 0 && sent == 0)
  72.             usable = 1;     /* Closed window probe */
  73.         else
  74.             usable = 0;     /* Window closed or shrunken */
  75.  
  76.         /* Compute size of segment we *could* send. This is the
  77.          * smallest of the usable window, the mss, or the amount
  78.          * we have on hand. (I don't like optimistic windows)
  79.          */
  80.         ssize = min(tcb->sndcnt - sent,usable);
  81.         ssize = min(ssize,tcb->mss);
  82.  
  83.         /* Now we decide if we actually want to send it.
  84.          * Apply John Nagle's "single outstanding segment" rule.
  85.          * If data is already in the pipeline, don't send
  86.          * more unless it is MSS-sized or the very last packet.
  87.          */
  88.         if(sent != 0 && ssize < tcb->mss
  89.          && !(tcb->state == TCP_FINWAIT1 && ssize == tcb->sndcnt-sent)){
  90.             ssize = 0;
  91.         }
  92.         /* Unless the tcp syndata option is on, inhibit data until
  93.          * our SYN has been acked. This ought to be OK, but some
  94.          * old TCPs have problems with data piggybacked on SYNs.
  95.          */
  96.         if(!tcb->flags.synack && !Tcp_syndata){
  97.             if(tcb->snd.ptr == tcb->iss)
  98.                 ssize = min(1,ssize);   /* Send only SYN */
  99.             else
  100.                 ssize = 0;      /* Don't send anything */
  101.         }
  102.         if(ssize == 0 && !tcb->flags.force)
  103.             break;          /* No need to send anything */
  104.  
  105.         tcb->flags.force = 0;   /* Only one forced segment! */
  106.  
  107.         seg.source = tcb->conn.local.port;
  108.         seg.dest = tcb->conn.remote.port;
  109.  
  110.         /* Set the flags according to the state we're in. It is
  111.          * assumed that if this segment is associated with a state
  112.          * transition, then the state change will already have been
  113.          * made. This allows this routine to be called from a
  114.          * retransmission timeout with force=1.
  115.          */
  116.         seg.flags.urg = 0; /* Not used in this implementation */
  117.         seg.flags.rst = 0;
  118.         seg.flags.ack = 1; /* Every state except TCP_SYN_SENT */
  119.         seg.flags.syn = 0; /* syn/fin/psh set later if needed */
  120.         seg.flags.fin = 0;
  121.         seg.flags.psh = 0;
  122.         seg.flags.congest = tcb->flags.congest;
  123.  
  124.         hsize = TCPLEN; /* Except when SYN being sent */
  125.         seg.mss = 0;
  126.         seg.optlen = 0;
  127.  
  128.         if(tcb->state == TCP_SYN_SENT)
  129.             seg.flags.ack = 0; /* Haven't seen anything yet */
  130.  
  131.         dsize = ssize;
  132.         if(!tcb->flags.synack && tcb->snd.ptr == tcb->iss){
  133.             /* Send SYN */
  134.             seg.flags.syn = 1;
  135.             dsize--;        /* SYN isn't really in snd queue */
  136.             /* Also send MSS */
  137.             seg.mss = Tcp_mss;
  138.             seg.optlen = 0;
  139.             hsize = TCPLEN + MSS_LENGTH;
  140.         }
  141.         seg.seq = tcb->snd.ptr;
  142.         seg.ack = tcb->rcv.nxt;
  143.         seg.wnd = tcb->rcv.wnd;
  144.         seg.up = 0;
  145.  
  146.         /* Now try to extract some data from the send queue. Since
  147.          * SYN and FIN occupy sequence space and are reflected in
  148.          * sndcnt but don't actually sit in the send queue, dup_p
  149.          * will return one less than dsize if a FIN needs to be sent.
  150.          */
  151.         if(dsize != 0){
  152.             int16 offset;
  153.  
  154.             /* SYN doesn't actually take up space on the sndq,
  155.              * so take it out of the sent count
  156.              */
  157.             offset = sent;
  158.             if(!tcb->flags.synack && sent != 0)
  159.                 offset--;
  160.  
  161.             if(dup_p(&dbp,tcb->sndq,offset,dsize) != dsize){
  162.                 /* We ran past the end of the send queue;
  163.                  * send a FIN
  164.                  */
  165.                 seg.flags.fin = 1;
  166.                 dsize--;
  167.             }
  168.         } else {
  169.             dbp = NULLBUF;
  170.         }
  171.         /* Allocate enough space for headers to avoid lots of
  172.          * little calls to malloc by pushdown()
  173.          */
  174.         hbp = ambufw(TCP_HDR_PAD);
  175.         hbp->data += TCP_HDR_PAD;
  176.         hbp->next = dbp;
  177.         dbp = hbp;
  178.  
  179.         /* If the entire send queue will now be in the pipe, set the
  180.          * push flag
  181.          */
  182.         if(dsize != 0 && sent + ssize == tcb->sndcnt)
  183.             seg.flags.psh = 1;
  184.  
  185.         /* If this transmission includes previously transmitted data,
  186.          * snd.nxt will already be past snd.ptr. In this case,
  187.          * compute the amount of retransmitted data and keep score
  188.          */
  189.         if(tcb->snd.ptr < tcb->snd.nxt)
  190.             tcb->resent += min(tcb->snd.nxt - tcb->snd.ptr,ssize);
  191.  
  192.         tcb->snd.ptr += ssize;
  193.         /* If this is the first transmission of a range of sequence
  194.          * numbers, record it so we'll accept acknowledgments
  195.          * for it later
  196.          */
  197.         if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
  198.             tcb->snd.nxt = tcb->snd.ptr;
  199.  
  200.         /* Fill in fields of pseudo IP header */
  201.         ph.source = tcb->conn.local.address;
  202.         ph.dest = tcb->conn.remote.address;
  203.         ph.protocol = TCP_PTCL;
  204.         ph.length = hsize + dsize;
  205.  
  206.         /* Generate TCP header, compute checksum, and link in data */
  207.         if((hbp = htontcp(&seg,dbp,&ph)) == NULLBUF){
  208.             free_p(dbp);
  209.             return;
  210.         }
  211.         /* If we're sending some data or flags, start retransmission
  212.          * and round trip timers if they aren't already running.
  213.          */
  214.         if(ssize != 0){
  215.             /* Set round trip timer. */
  216.             rto = mybackoff(tcb->backoff) * (4 * tcb->mdev + tcb->srtt);
  217.             set_timer(&tcb->timer,max(MIN_RTO,rto));
  218.             if(!run_timer(&tcb->timer))
  219.                 start_timer(&tcb->timer);
  220.  
  221.             /* If round trip timer isn't running, start it */
  222.             if(!tcb->flags.rtt_run){
  223.                 tcb->flags.rtt_run = 1;
  224.                 tcb->rtt_time = msclock();
  225.                 tcb->rttseq = tcb->snd.ptr;
  226.             }
  227.         }
  228.         if(tcb->flags.retran)
  229.             tcpRetransSegs++;
  230.         else
  231.             tcpOutSegs++;
  232.  
  233.         ip_send(tcb->conn.local.address,tcb->conn.remote.address,
  234.          TCP_PTCL,tcb->tos,0,hbp,ph.length,0,0);
  235.     }
  236. }
  237.